package com.jl.util;

import java.math.BigInteger;

public class Calculator {

	/**
	 * lsh,left move the data
	 * 
	 * @param data
	 * @param round
	 *            time
	 * @return
	 */
	public static byte[] lsh(byte[] data, int round) {
		byte[] copy = data.clone();
		for (int i = 0; i < round; i++) {
			lshone(copy);
		}
		return copy;
	}

	private static void lshone(byte[] data) {
		for (int i = 0; i < data.length - 1; i++) {
			data[i] <<= 1;
			if ((data[i + 1] & 0x80) != 0) {
				data[i] |= 0x01;
			}
		}
		data[data.length - 1] <<= 1;
	}

	/**
	 * rsh
	 * 
	 * @param data
	 * @param round
	 * @return
	 */
	public static byte[] rsh(byte[] data, int round) {
		byte[] copy = data.clone();
		for (int i = 0; i < round; i++) {
			rshone(copy);
		}
		return copy;
	}

	private static void rshone(byte[] data) {
		for (int i = data.length - 1; i > 0; i--) {
			data[i] = (byte) ((data[i] & 0xFF) >> 1);
			if ((data[i - 1] & 0x01) != 0) {
				data[i] |= 0x80;
			}
		}
		data[0] = (byte) ((data[0] & 0xFF) >> 1);
	}

	/**
	 * not
	 * 
	 * @param data
	 * @return
	 */
	public static byte[] not(byte[] data) {
		byte[] result = new byte[data.length];
		for (int i = 0; i < data.length; i++) {
			result[i] = (byte) ~data[i];
		}
		return result;
	}

	/**
	 * rol
	 * 
	 * @param data
	 * @param round
	 * @return
	 */
	public static byte[] rol(byte[] data, int round) {
		byte[] copy = data.clone();
		for (int i = 0; i < round; i++) {
			rolone(copy);
		}
		return copy;
	}

	private static void rolone(byte[] data) {
		byte temp = data[0];
		for (int i = 0; i < data.length - 1; i++) {
			data[i] <<= 1;
			if ((data[i + 1] & 0x80) != 0) {
				data[i] |= 0x01;
			}
		}
		data[data.length - 1] <<= 1;
		if ((temp & 0x80) != 0) {
			data[data.length - 1] |= 0x01;
		}
	}

	/**
	 * ror
	 * 
	 * @param data
	 * @param round
	 * @return
	 */
	public static byte[] ror(byte[] data, int round) {
		byte[] copy = data.clone();
		for (int i = 0; i < round; i++) {
			rorone(copy);
		}
		return copy;
	}

	private static void rorone(byte[] data) {
		byte temp = data[data.length - 1];
		for (int i = data.length - 1; i > 0; i--) {
			data[i] = (byte) ((data[i] & 0xFF) >> 1);
			if ((data[i - 1] & 0x01) != 0) {
				data[i] |= 0x80;
			}
		}
		data[0] = (byte) ((data[0] & 0xFF) >> 1);
		if ((temp & 0x01) != 0) {
			data[0] |= 0x80;
		}
	}

	/**
	 * xor
	 * 
	 * @param data1
	 * @param data2
	 * @return
	 */
	public static byte[] xor(byte[] data1, byte[] data2) {
		byte[] result = new byte[data1.length];
		if(data2 == null || data2.length == 0){
			data2 = new byte[1];
		}
		for (int i = 0, j = 0; i < data1.length; i++) {
			result[i] = (byte) ((data1[i] ^ data2[j]) & 0xFF);
			j++;
			j %= data2.length;
		}
		return result;
	}

	/**
	 * or
	 * 
	 * @param data1
	 * @param data2
	 * @return
	 */
	public static byte[] or(byte[] data1, byte[] data2) {
		byte[] result = new byte[data1.length];
		if(data2 == null || data2.length == 0){
			data2 = new byte[1];
		}
		for (int i = 0, j = 0; i < data1.length; i++) {
			result[i] = (byte) ((data1[i] | data2[j]) & 0xFF);
			j++;
			j %= data2.length;
		}
		return result;
	}

	/**
	 * and
	 * 
	 * @param data1
	 * @param data2
	 * @return
	 */
	public static byte[] and(byte[] data1, byte[] data2) {
		byte[] result = new byte[data1.length];
		if(data2 == null || data2.length == 0){
			data2 = new byte[1];
		}
		for (int i = 0, j = 0; i < data1.length; i++) {
			result[i] = (byte) ((data1[i] & data2[j]) & 0xFF);
			j++;
			j %= data2.length;
		}
		return result;
	}

	/**
	 * add
	 * 
	 * @param data1
	 * @param data2
	 * @return
	 */
	public static byte[] add(byte[] data1, byte[] data2) {
		String strP1 = StrUtil.trimStrToZero(HexUtil.toHexString(data1));
		String strP2 = StrUtil.trimStrToZero(HexUtil.toHexString(data2));
		BigInteger bi1 = new BigInteger(HexUtil.compact(strP1), 16);
		BigInteger bi2 = new BigInteger(HexUtil.compact(strP2), 16);
		BigInteger bi = bi1.add(bi2);
		return bi.toByteArray();
	}

	/**
	 * sub
	 * 
	 * @param data1
	 * @param data2
	 * @return
	 */
	public static byte[] sub(byte[] data1, byte[] data2) {
		String strP1 = StrUtil.trimStrToZero(HexUtil.toHexString(data1));
		String strP2 = StrUtil.trimStrToZero(HexUtil.toHexString(data2));
		BigInteger bi1 = new BigInteger(HexUtil.compact(strP1), 16);
		BigInteger bi2 = new BigInteger(HexUtil.compact(strP2), 16);
		BigInteger bi = bi1.subtract(bi2);
		return bi.toByteArray();
	}

	/**
	 * multiply
	 * 
	 * @param data1
	 * @param data2
	 * @return
	 */
	public static byte[] mul(byte[] data1, byte[] data2) {
		String strP1 = StrUtil.trimStrToZero(HexUtil.toHexString(data1));
		String strP2 = StrUtil.trimStrToZero(HexUtil.toHexString(data2));
		BigInteger bi1 = new BigInteger(HexUtil.compact(strP1), 16);
		BigInteger bi2 = new BigInteger(HexUtil.compact(strP2), 16);
		BigInteger bi = bi1.multiply(bi2);
		return bi.toByteArray();
	}

	/**
	 * div
	 * 
	 * @param data1
	 * @param data2
	 * @return
	 */
	public static byte[] div(byte[] data1, byte[] data2) {
		String strP1 = StrUtil.trimStrToZero(HexUtil.toHexString(data1));
		String strP2 = StrUtil.trimStrToZero(HexUtil.toHexString(data2));
		BigInteger bi1 = new BigInteger(HexUtil.compact(strP1), 16);
		BigInteger bi2 = new BigInteger(HexUtil.compact(strP2), 16);
		if (bi2.equals(BigInteger.ZERO)) {
			return new byte[0];
		}
		BigInteger bi = bi1.divide(bi2);
		return bi.toByteArray();
	}

	/**
	 * mod
	 * 
	 * @param data1
	 * @param data2
	 * @return
	 */
	public static byte[] mod(byte[] data1, byte[] data2) {
		String strP1 = StrUtil.trimStrToZero(HexUtil.toHexString(data1));
		String strP2 = StrUtil.trimStrToZero(HexUtil.toHexString(data2));
		BigInteger bi1 = new BigInteger(HexUtil.compact(strP1), 16);
		BigInteger bi2 = new BigInteger(HexUtil.compact(strP2), 16);
		if (bi2.equals(BigInteger.ZERO)) {
			return new byte[0];
		}
		BigInteger bi = bi1.mod(bi2);
		return bi.toByteArray();
	}

}
